<template>
  <div class="universal-editor-container">
    <div class="editor-toolbar">
      <!-- Keep Apply button for now, consider moving trigger later -->
      <el-button :icon="Refresh" @click="applyChanges" :disabled="!editorStore.hasContent">应用到可视化</el-button>
      <!-- Remove Format XML button, maybe add generic format later -->
      <!-- <el-button :icon="MagicStick" @click="formatCode" :disabled="!editorStore.hasLoadedFile">格式化 XML</el-button> -->
      <el-alert v-if="editorStore.error" type="error" show-icon :closable="false" class="editor-error-alert">
        <template #title>解析/格式化错误</template>
        {{ editorStore.error }}
      </el-alert>
    </div>
    <div ref="editorContainer" class="editor-instance"></div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed, shallowRef, nextTick } from 'vue';
import * as monaco from 'monaco-editor';
import { debounce } from 'lodash-es';
import { storeToRefs } from 'pinia';
import { useUniversalEditorStore } from '@/stores/universalEditorStore';
import { useUniversalSettingsStore } from '@/stores/universalSettingsStore';
import { useUniversalVisualizerSettingsStore } from '@/stores/universalVisualizerSettingsStore';
import { parse as parseWithFastXml } from '@/parsers/universal/fastXmlUniversalParser';
import { ElButton, ElAlert, ElMessage } from 'element-plus'; // Import needed components
import { Refresh } from '@element-plus/icons-vue'; // Import needed icons

// --- Store Instances ---
const editorStore = useUniversalEditorStore();
const globalSettingsStore = useUniversalSettingsStore();
const visualizerSettingsStore = useUniversalVisualizerSettingsStore();

// --- Use storeToRefs for reactive access to state/getters ---
const { darkMode } = storeToRefs(globalSettingsStore);
const { readOnly, currentContent, error: editorStoreError, hasLoadedFile } = storeToRefs(editorStore); // Renamed error to avoid conflict
const { showMiniMap, wordWrap, enableRealtimeXmlValidation } = storeToRefs(visualizerSettingsStore);

// --- Refs ---
const editorContainer = ref<HTMLElement | null>(null);
// Use direct monaco type
const editorInstance = shallowRef<monaco.editor.IStandaloneCodeEditor | null>(null);

// --- Map store format to Monaco language ID ---
const editorLanguage = computed(() => {
  switch (editorStore.currentFileInfo.sourceFormat) {
    case 'json': return 'json';
    case 'yaml': return 'yaml';
    case 'xml': return 'xml';
    case 'toml': return 'ini'; // Monaco uses 'ini' for TOML highlighting
    case 'javascript': return 'javascript';
    case 'typescript': return 'typescript';
    default: return 'plaintext';
  }
});

// Helper function to parse error string and set markers (Keep for external/XML errors)
// Use direct monaco types
function parseErrorAndSetMarkers(error: string, model: monaco.editor.ITextModel | null, owner: string) {
    // --- RE-ADD Fallback Logic --- 
    console.log(`[parseErrorAndSetMarkers] Received error for owner '${owner}':`, error); // Log received error
    if (!model || !error) { // Also check if error string is empty
        console.log(`[parseErrorAndSetMarkers] Clearing markers for owner '${owner}'. Model exists: ${!!model}, Error exists: ${!!error}`);
        if(model) monaco.editor.setModelMarkers(model, owner, []); // Clear existing markers if no error
        return;
    }

    const markers: monaco.editor.IMarkerData[] = [];
    // --- FINAL REGEX Attempt --- 
    // Focus only on extracting Line and Col from the standard format
    const errorRegex = /\(Line:\s*(\d+),\s*Col:\s*(\d+)\)/;
    // Explanation:
    // \(Line:\s* - Match "(Line:" + optional space
    // (\d+)      - Capture Line number (Group 1)
    // ,\s*Col:\s* - Match ", Col:" + optional space
    // (\d+)      - Capture Column number (Group 2)
    // \)         - Match closing parenthesis

    let regexMatched = false; // Flag to track if regex matched
    let firstMatchResult: RegExpMatchArray | null = null; // Store the first match

    // We only need to find the *first* occurrence containing Line/Col info usually
    const match = error.match(errorRegex);

    if (match) {
        regexMatched = true; // Mark as matched
        firstMatchResult = match; // Store the result
        console.log(`[parseErrorAndSetMarkers] Regex matched error string.`); // Log regex match
    }

    // If regex matched, create a marker with extracted info
    if (regexMatched && firstMatchResult) {
        const severity = monaco.MarkerSeverity.Error; // Assume Error
        const lineNumber = parseInt(firstMatchResult[1], 10); // Group 1 is Line
        const column = parseInt(firstMatchResult[2], 10);     // Group 2 is Column
        const message = error.trim(); // Use the full error message

        // Basic validation for line/column numbers
        if (lineNumber > 0 && column > 0) { 
            markers.push({
                severity: severity,
                message: message,
                startLineNumber: lineNumber,
                startColumn: column,
                endLineNumber: lineNumber,
                // Try to highlight a small range, fallback to 1 char if column is last
                endColumn: model.getLineMaxColumn(lineNumber) > column ? column + 1 : column, 
            });
        } else {
             console.warn(`[parseErrorAndSetMarkers] Parsed invalid Line/Col: ${lineNumber}/${column}. Falling back to general marker.`);
             regexMatched = false; // Treat as non-match if numbers invalid
        }
    }
    
    // Fallback: If regex didn't match OR parsed invalid numbers, create a general marker
    if (!regexMatched && error.trim()) {
        console.warn(`[parseErrorAndSetMarkers] Regex did NOT match or parsed invalid info for owner '${owner}'. Creating general marker.`); // Log fallback activation
        markers.push({
            severity: monaco.MarkerSeverity.Error, // Assume error for general issues
            message: error.trim(),
            startLineNumber: 1,
            startColumn: 1,
            endLineNumber: 1,
            endColumn: model.getLineMaxColumn(1) // Mark the first line
        });
    }
    // --- END RE-ADD --- 

    console.log(`[parseErrorAndSetMarkers] Generated ${markers.length} markers for owner '${owner}':`, JSON.parse(JSON.stringify(markers))); // Log markers before setting

    // Set markers if any were found (or clear if markers is empty)
    console.log(`[parseErrorAndSetMarkers] Calling setModelMarkers for owner '${owner}'...`);
    monaco.editor.setModelMarkers(model, owner, markers);
    console.log(`[parseErrorAndSetMarkers] setModelMarkers called for owner '${owner}'.`);
}

// --- Realtime XML Validation Logic (Keep) ---
const validateXmlContent = () => {
    console.log("[validateXmlContent] Function executed."); // <-- Log execution start
    const model = editorInstance.value?.getModel();
    if (!model) return; // Exit if no model

    // Original logic continues below
    if (editorLanguage.value !== 'xml') { // Clear if model gone or lang not XML
        console.log("[validateXmlContent] Language is not XML, clearing markers.");
        monaco.editor.setModelMarkers(model, 'xml-validator', []);
        return;
    }
    // Use ref from storeToRefs
    if (!enableRealtimeXmlValidation.value) {
        console.log("[validateXmlContent] Realtime validation disabled, clearing markers.");
        monaco.editor.setModelMarkers(model, 'xml-validator', []); // Clear if validation disabled
        return;
    }

    const content = model.getValue();
    if (!content.trim()) { // Clear markers if empty
        console.log("[validateXmlContent] Content is empty, clearing markers.");
        monaco.editor.setModelMarkers(model, 'xml-validator', []);
        return;
    }

    // --- REVISED LOGIC: Check return value of parseWithFastXml --- 
    console.log("[validateXmlContent] Content being passed to parseWithFastXml:", JSON.stringify(content));
    const parseResult = parseWithFastXml(content);

    if (!parseResult.success) {
        console.log("[validateXmlContent] parseWithFastXml reported failure.");
        // Use the error message from the result
        parseErrorAndSetMarkers(parseResult.error || 'Unknown XML parsing error', model, 'xml-validator');
    } else {
        console.log("[validateXmlContent] parseWithFastXml reported success. Clearing markers.");
        // Clear markers if parsing was successful according to parseWithFastXml
        monaco.editor.setModelMarkers(model, 'xml-validator', []);
    }
    // --- END REVISED LOGIC --- 
};
const debouncedValidateXml = debounce(validateXmlContent, 750);

// --- Lifecycle Hooks and Watchers ---
onMounted(() => {
    console.log('[UniversalEditor] onMounted hook started.'); // Log start
    if (editorContainer.value) {
        console.log('[UniversalEditor] editorContainer found:', editorContainer.value); // Log container element

        // Configure Monaco JSON defaults (Keep, basic settings)
        monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
            validate: true, // Use Monaco's built-in basic validation
            allowComments: false,
            schemas: [],
        });

        editorInstance.value = monaco.editor.create(editorContainer.value, {
            model: null,
            language: 'plaintext',
            // Use refs from storeToRefs
            theme: darkMode.value ? 'vs-dark' : 'vs',
            automaticLayout: true,
            glyphMargin: true,
            minimap: { enabled: showMiniMap.value },
            wordWrap: wordWrap.value ? 'on' : 'off',
            readOnly: readOnly.value,
        });

        console.log('[UniversalEditor] Monaco editor instance created:', editorInstance.value); // Log instance

        // Set initial content if available
        // Use ref from storeToRefs
        if (currentContent.value) {
            console.log('[UniversalEditor] Setting initial content...'); // Log initial content block
            // Use ref from storeToRefs
            const currentModel = monaco.editor.createModel(
                currentContent.value,
                editorLanguage.value,
                // Use direct monaco type
                monaco.Uri.parse(`file:///current.${editorStore.currentFileInfo.sourceFormat || 'txt'}`)
            );
            editorInstance.value.setModel(currentModel);
            console.log('[UniversalEditor] Initial model set.'); // Log model set

            // Clear markers initially
            monaco.editor.setModelMarkers(currentModel, 'xml-validator', []);
            monaco.editor.setModelMarkers(currentModel, 'json', []); // Also clear potential JSON markers
            monaco.editor.setModelMarkers(currentModel, 'external-error', []);

            // Initial XML validation
            console.log('[UniversalEditor] Triggering initial validation check...');
            validateXmlContent();
        } else {
            console.log('[UniversalEditor] No initial content found in store.'); // Log no initial content
        }

        // Listen for content changes
        editorInstance.value.onDidChangeModelContent((/* e */) => { // Explicitly ignore 'e' if not used
            const newContent = editorInstance.value?.getValue() || '';
            // Call the correct action
            editorStore.handleEditorChange(newContent);

             // Trigger XML validation if needed
            if (editorLanguage.value === 'xml') {
                 console.log("[onDidChangeModelContent] Language is XML, calling debouncedValidateXml..."); // <-- Log before debounce call
                 debouncedValidateXml();
             }
        });

        // Handle external errors (Keep)
        // Use ref from storeToRefs
        if (editorStoreError.value) {
            const model = editorInstance.value?.getModel();
            if (model) {
                 // Use ref from storeToRefs
                 parseErrorAndSetMarkers(editorStoreError.value, model, 'external-error');
            }
        }

    } else {
        console.error("[UniversalEditor] Editor container (ref) not found on mount!"); // Log container not found
    }
});

onBeforeUnmount(() => {
    editorInstance.value?.dispose();
    editorInstance.value = null;
});

// Watch for theme changes (Keep)
// Use ref from storeToRefs
watch(darkMode, (isDark) => {
    monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');
});

// Watch for minimap changes (Keep)
// Use ref from storeToRefs
watch(showMiniMap, (show) => {
    editorInstance.value?.updateOptions({ minimap: { enabled: show } });
});

// Watch for word wrap changes (Keep)
// Use ref from storeToRefs
watch(wordWrap, (wrap) => {
    editorInstance.value?.updateOptions({ wordWrap: wrap ? 'on' : 'off' });
});

// Watch for read-only changes (Keep)
// Use ref from storeToRefs
watch(readOnly, (newReadOnly) => {
    editorInstance.value?.updateOptions({ readOnly: newReadOnly });
});

// Watch for external content changes (e.g., file loaded)
// Use ref from storeToRefs
watch(currentContent, (newContent, oldContent) => {
    if (editorInstance.value && newContent !== editorInstance.value.getValue()) {
        let model = editorInstance.value.getModel();
        const newLang = editorLanguage.value;
        // Use direct monaco type
        const newUri = monaco.Uri.parse(`file:///current.${editorStore.currentFileInfo.sourceFormat || 'txt'}`);

        if (model && model.getLanguageId() === newLang && model.uri.toString() === newUri.toString()) {
            model.pushEditOperations([], [{
                range: model.getFullModelRange(),
                text: newContent || ''
            }], () => null);
        } else {
            model?.dispose();
            // Use direct monaco type
            model = monaco.editor.createModel(newContent || '', newLang, newUri);
            editorInstance.value.setModel(model);
        }

        // Reset markers when content changes externally
        if(model) {
            monaco.editor.setModelMarkers(model, 'xml-validator', []);
            monaco.editor.setModelMarkers(model, 'json', []); // Clear potential JSON markers
            monaco.editor.setModelMarkers(model, 'external-error', []);
        }

        // Trigger initial XML validation
        if (newLang === 'xml') {
             validateXmlContent();
        }
    }
});

// Watch for language changes (derived from file info)
watch(editorLanguage, (newLang, oldLang) => {
    const model = editorInstance.value?.getModel();
    if (model && editorInstance.value) {
        monaco.editor.setModelLanguage(model, newLang);

        // Clear markers from previous language/validator
        monaco.editor.setModelMarkers(model, 'xml-validator', []);
        monaco.editor.setModelMarkers(model, 'json', []);
        monaco.editor.setModelMarkers(model, 'external-error', []);

        // Trigger initial XML validation if needed
         if (newLang === 'xml') {
             validateXmlContent();
         }
    }
});

// Watch for external error changes (Keep)
// Use ref from storeToRefs
watch(editorStoreError, (newError) => {
    const model = editorInstance.value?.getModel();
    if (model) {
        if (newError) {
            parseErrorAndSetMarkers(newError, model, 'external-error');
        } else {
            monaco.editor.setModelMarkers(model, 'external-error', []);
        }
    }
});

// Watch for real-time XML validation setting change (Keep)
// Use ref from storeToRefs
watch(enableRealtimeXmlValidation, (enabled) => {
    // Trigger validation logic which now handles clearing markers if disabled
    validateXmlContent();
});

// --- Toolbar Actions ---
const applyChanges = async () => {
  if (!editorStore.hasContent) {
    // Use ElMessage
    ElMessage.warning('编辑器中没有内容可应用。');
    return;
  }
  // Use ElMessage
  ElMessage.info('正在应用更改到可视化...');
  const success = await editorStore.applyEditorChangesToVisualizer();
  if (!success && editorStoreError.value) {
    // Error likely already shown
  } else if (!success) {
     ElMessage.error('应用更改失败，请检查编辑器内容。');
  }
};

// --- Expose Methods (if needed via defineExpose) ---
// defineExpose({ /* methods */ });

</script>

<style scoped>
/* Styles are copied from JDFEditor, can be adjusted */
.universal-editor-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.editor-toolbar {
  padding: 5px 10px;
  border-bottom: 1px solid var(--el-border-color);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

.editor-instance {
  flex-grow: 1;
  overflow: hidden; /* Monaco handles its own scrolling */
}

.editor-error-alert {
  margin-left: auto; /* Push error alert to the right */
}
</style> 